package com.cyx.gametool.db;

import cn.hutool.log.StaticLog;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * <pre>
 *  mysql连接池
 * </pre>
 *
 * @Author cyx
 * @Date 2023/7/17 18:35
 **/
public class HikariDBPool {

    private final static String dbUrl = "jdbc:mysql://%s:%s/%s?characterEncoding=utf-8&autoReconnect=true&useSSL=false&socketTimeout=10000";
    // 数据库配置
    private final DBConfig dbConfig;
    // 数据库连接池
    private HikariDataSource dbPool = null;
    // 错误次数
    private int errCount;

    public HikariDBPool(DBConfig dbConfig) throws Exception {
        this.dbConfig = dbConfig;
        init(dbConfig);
    }

    public static void main(String[] args) {
//        SqlExecutor.executeBatch()
    }

    /**
     * <pre>
     * 初始化
     * </pre>
     */
    private void init(DBConfig dbConfig) throws Exception {
        Connection connection = null;
        try {
            initDBPool(dbConfig);
            connection = dbPool.getConnection();
            if (connection == null) {
                throw new Exception("DBPool connection error,dbConfig:" + dbConfig);
            }
        } catch (Exception e) {
            throw new Exception("DBPool init error,dbConfig:" + dbConfig, e);
        } finally {
            if (connection != null) {
                try {
                    connection.close();
                } catch (SQLException e) {
                    StaticLog.error(e, "DBPool close error,dbConfig:" + dbConfig);
                }
            }
        }
    }

    /**
     * <pre>
     * 初始化数据连接池
     * </pre>
     */
    private void initDBPool(DBConfig dbConfig) {
        HikariConfig config = new HikariConfig();
        config.setJdbcUrl(String.format(dbUrl, dbConfig.getHost(), dbConfig.getPort(), dbConfig.getDatabase()));
        config.setUsername(dbConfig.getUser());
        config.setPassword(dbConfig.getPassword());
        // 开启预编译(相同sql不用每次都编译)
        config.addDataSourceProperty("cachePrepStmts", "true");
        // 预编译缓存数量
        config.addDataSourceProperty("prepStmtCacheSize", "256");
        // 预编译sql长度限制
        config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");
        // 获取连接超时ms
        config.setConnectionTimeout(5000);
        // 泄露探测时间ms
        config.setLeakDetectionThreshold(15000);
        // 初始化数据连接池
        dbPool = new HikariDataSource(config);
    }

    /**
     * <pre>
     * 获取数据库连接
     * </pre>
     */
    public Connection getConnection() {
        try {
            return dbPool.getConnection();
        } catch (SQLException e) {
            StaticLog.error(e, "getConnection error,db:" + dbConfig);
        }
        return null;
    }

    /**
     * <pre>
     * 停止数据连接池
     * </pre>
     */
    public void close() {
        if (dbPool != null) {
            dbPool.close();
        }
    }

    /**
     * <pre>
     * 检测连接池
     * </pre>
     */
    public boolean checkDB(DBConfig dbConfig) {
        PreparedStatement pstmt = null;
        Connection conn = null;
        ResultSet rs = null;
        try {
            conn = dbPool.getConnection();
            if (conn == null) {
                StaticLog.error("check DBPool connection error,dbConfig:" + dbConfig);
                return false;
            }
            pstmt = conn.prepareStatement("SELECT 1;");
            rs = pstmt.executeQuery();
            if (rs.next()) {
                return rs.getInt(1) == 1;
            }
        } catch (Exception e) {
            StaticLog.error(e, "check DBPool error,errorCount:{},dbConfig:{}", errCount, dbConfig);
            if (++errCount % 4 == 0) {
                dbPool.close();
                try {
                    init(dbConfig);
                    return true;
                } catch (Exception ex) {
                    StaticLog.error(e, "DBPool error,errorCount:{},dbConfig:{}", errCount, dbConfig);
                }
            }
            return false;
        } finally {
            close(conn, pstmt, rs);
        }
        return true;
    }

    public void close(Connection conn, PreparedStatement pstmt, ResultSet rs) {
        try {
            if (rs != null) {
                rs.close();
            }
        } catch (Exception e) {
            StaticLog.error(e, "");
        }
        try {
            if (pstmt != null) {
                pstmt.clearParameters();
                pstmt.close();
            }
        } catch (Exception e) {
            StaticLog.error(e, "");
        }
        try {
            if (conn != null) {
                conn.close();
            }
        } catch (Exception e) {
            StaticLog.error(e, "");
        }
    }
}
